<?php
/**
 *
 *
 */
abstract class Yz_Module_WebModule
    extends Yz_Module_Abstract
    implements Yz_Module_WebModule_Interface
{

    protected $webPath;

    const DEFAULT_CONTROLLER = "Default";
    const ERROR_CONTROLLER = false;

    /**
     *  @return string
     */
    public function getWebPath()
    {
        return $this->webPath;
    }

    /**
     *  @return this
     */
    public function setWebPath($path)
    {
        assert('is_string($path)');
        $this->webPath = $path;
        return $this;
    }

    /**
     *
     */
    public function route(Yz_Array $args, Yz_Hash $post=null)
    {
        $ctlr = null;
        if (! $args->isEmpty()) {
            $ctrlName = Yz_String::toCamelCase($args[0]);
            if (Yz_String::isValidIdentifier($ctrlName)
                    && $this->hasController($ctrlName)) {
                $ctlr = $this->selectController($ctrlName);

                // real selector
                $ctlrSelector = $args->shift() .'/';
            }
        }
        if (! $ctlr)
            $ctlr = $this->selectDefaultController();

        try {
            return $this->execController($ctlr, $args, $post);
        } catch (Yz_Exception $ex) {
            try {
                $r = $ctlr->doError($ex);
            } catch (Yz_Exception $ex) {
                // if ctlr's error handler throws exception,
                // it will be handled
                $r = false;
            }
            return $r ?: $this->routeError($ex);
        }
    }



    /**
     *
     */
    public function routeError(Yz_Exception $ex)
    {
        $ctlr = $this->selectErrorController();
        if (! $ctlr)
            throw $ex;
        return $ctlr->doError($ex);
    }


    /**
     *  @return Yz_Controller_Interface
     */
    protected function execController($ctlr, $args, $post)
    {

        if ($post) {
            $r = $ctlr->doPost($args, $post);
        } else {
            $r = $ctlr->doGet($args);
        }
        switch (true) {
            case false === $r:
                // restart routing process
                return false;
            case is_string($r):
                // select controller by the name $r
                $ctlr = $this->selectController($r);
                return $this
                    ->execController($ctlr, $args, $post)
                ;
            default:
        }
        assert('$r instanceof Yz_Controller_Interface');
        return $r;
    }

    /** try to find a controller specified by a string convertible to an identifier
     *      (@see Yz_String::identifier)
     *  @return string|bool(false)
     */
    public function hasController($ctlrName)
    {
        assert('is_string($ctlrName) and "" !== $ctlrName');
        debug($ctlrName);
        return $this->resolve( 'Controller_' . ucfirst($ctlrName), $throw=false );
    }

    /** find a controller by exact name
     *  @return Yz_Controller_Interface&
     *  @throws Yz_Module_Exception_ControllerNotFound
     */
    public function selectController($ctlrName)
    {
        debug($ctlrName);
        $class = $this->hasController($ctlrName);
        if (false === $class) {
            throw Yz_Module_Exception_ControllerNotFound::_new(array(
                'moduleName' => $this->fullName,
                'ctrlName' => $ctlrName,
            )) ;
        }
        assert('is_subclass_of($class,"Yz_Controller_Interface")');
        return new $class($this);
    }



    /** Select a default controller (usually, when a named controller not found)
     *      It may be an error controller ("controller $name not found")
     *      or module contents generator
     *      or a dispatcher
     *  @return Yz_Controller_Interface&
     *  @todo if DEFAULT_CONTROLLER is false, throw SmthNotFound => 404
     *  @throws Yz_Module_Exception_ClassNotFound
     *      because having specified a DEFAULT_CONTROLLER one assumed that it exists
     */
    public function selectDefaultController()
    {
        $default = static::DEFAULT_CONTROLLER;
        if (! $default)
            throw new Yz_Exception_NotImplemented();

        $class = $this->resolve( 'Controller_' . $default, $throw=true );
        assert('is_subclass_of($class,"Yz_Controller_Interface")');
        return new $class($this);
    }

    public function selectErrorController()
    {
        $default = static::ERROR_CONTROLLER;
        if (! $default)
            return false;
        $class = $this->resolve( 'Controller_' . $default, $throw=true );
        assert('is_subclass_of($class,"Yz_Controller_Interface")');
        return new $class($this);
    }

    /** select a template by name
     *  @return Yz_Controller_Interface&
     *  @throws TemplateNotFound
     */
    public function selectTemplate($templateName)
    {
        assert('is_string($templateName) and "" !== $templateName');
        debug($templateName);

        // regardless of what $templateName is supplied, it must match a class
        $class = $this->resolve( 'Template_' . ucfirst($templateName), $throw=false );
        if (false === $class) {
            throw Yz_Module_Exception_TemplateNotFound::_new(array(
                'templateName' => $templateName,
                'moduleName' => $this->fullName,
            ));
        }
        assert('is_subclass_of($class,"Yz_Template_Interface")');
        return new $class($this);
    }

} // class
